/*
 * Copyright (c) 2023. All rights reserved.
 * Please don't try to modify the code, because I don't know what the consequences will be.
 *
 *
 *
 *
 *
 *
 *
 */

package cc.zzone.bean;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

/**
 * Socket工厂
 *
 * @author jinzhengze
 * @since 2022年8月2日
 */
@Slf4j
public final class SocketFactory {

    /**
     * 缓冲区buf大小
     */
    private static final Integer SIZE = 10485760;

    private SocketFactory() {
    }

    /**
     * 创建服务
     */
    public static void build(ServerInfo info) {
        Assert.notNull(info, "ServerInfo cannot be null");
        new Thread(() -> {
            log.info("### Socket Start! ### ThreadName: {}", Thread.currentThread().getName());
            EventLoopGroup bossGroup = new NioEventLoopGroup(1);
            EventLoopGroup workerGroup = new NioEventLoopGroup(1);
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(bossGroup, workerGroup);
                serverBootstrap.channel(NioServerSocketChannel.class);
                serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                        .option(ChannelOption.SO_RCVBUF, SIZE)
                        .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                        .handler(new LoggingHandler(LogLevel.INFO));
                serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childOption(ChannelOption.TCP_NODELAY, true)
                        .childOption(ChannelOption.SO_SNDBUF, SIZE)
                        .childOption(ChannelOption.SO_RCVBUF, SIZE)
                        .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) {
                                ChannelPipeline pipeline = socketChannel.pipeline();
                                if (info.getReaderIdleTime() > 0L)
                                    pipeline.addLast(new IdleStateHandler(
                                            info.getReaderIdleTime(), 0, 0, info.getTimeUnit()));
                                if (info.getHandlers() != null && info.getHandlers().size() > 0)
                                    for (ChannelHandler channelHandler : info.getHandlers())
                                        pipeline.addLast(channelHandler);
                            }
                        });
                ChannelFuture channelFuture = serverBootstrap.bind(info.getPort()).sync();
                log.info("### Start Succeed! ### ThreadName: {}, URI: {}",
                        Thread.currentThread().getName(), "127.0.0.1:" + info.getPort());
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException ignored) {
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
                log.info("### Socket Closed! ### ThreadName: {}", Thread.currentThread().getName());
            }
        }).start();
    }
}
